<html>
  <head>
    <title>插槽</title>
  </head>
  <body>
    <script>
      // 插槽

            /*
                一、概述

                        Vue 实现了一套内容分发的API， 将 "slot元素" 作为承载分发内容的出口。

                        它允许你想这样合成组件：

                                <template>
                                      <view>
                                          <componentA>
                                              Your Profile
                                          </componentA>
                                      </view>
                                </template>

                        在 <componentA>的模板中坑能会写为：

                                <template>
                                  <view>
                                    -- 我是子组件componentA --
                                    <view >{{title}}</view>
                                    <slot></slot>  // 插槽就是父组提供的内容在"子组件中的占位"
                                  </view>
                                </template>     

                        当组件渲染的时候， <slot></slot>将会被替换为 "Your Profile".

                        插槽内容的指定是在父组件里，插槽内可以是任何模板代码，包括HMTL：

                                 <template>
                                    <view>
                                      <!-- 我是父组件 --
                                      <componentA>
                                        <view>Your Profile</view>
                                        
                                            -- 插槽内容可以是任何使用  "父组件作用域" 的 "模板语法内容"---

                                            -- 添加一个 uni-icons 图标 --
                                        <uni-icons type="contact" size="30"></uni-icons>
                                      </componentA>
                                    </view>
                                  </template>

                         如果 <componentA> 的 tempalte 中没有包含一个 <slot> 元素，则该组件"起始标签" 和 "结束标签"

                         之间的任何内容都会被抛弃。
            */

            /*
                二、编译作用域

                        当你想在一个插槽中使用数据时，例如：

                              <navigation-link url="/profile">
                                  Logged in as {{ user.name }}
                              </navigation-link>

                        因为使用 <navigation-link>是在<navigation-link>的父组件内，所以指定内容的时候该插槽的内容里

                        像其<navigation-link>的父组的其他位置一样，可以访问<navigation-link>的父组件的作用域中的所有property。

                        但是，在指定子组件子（<navigation-link>组件）插槽中内容的时候，不能访问子组件的作用域。

                        (这是显然的嘛，因为指定子组件内容的位置是父组件的模板中，所以它不能访问到子组件的作用域)

                        例如：   url 是访问不到的：
     
                              <navigation-link url="/profile">
                                  Clicking here will send you to: {{ url }}
                                  <!--
                                    这里的 `url` 会是 undefined，
                                    因为其 (指该插槽的) 内容是在父组件的模板中传递给<navigation-link> 的
                                    而不是在 <navigation-link> 组件的模板中定义的。
                                  --
                              </navigation-link>
            */

            /*
                 三、默认内容

                        有的时候为一个插槽设置具体的后备（默认）内容是很有用的，它只会在没有提供内容的时候被渲染。

                        eg:  在一个 <submit-button> 组件中：
                             <template>
                                <button type="submit">
                                    <slot></slot>
                                </button>
                             </template>

                        我们可能希望这个 <button>内绝大多数情况下都渲染文本 "Submit" 。 为了将"Submit"作为后备内容，我们可以将它放在 <slot>标签内：
                            <template>
                                <button type="submit">
                                    <slot>Submit</slot>
                                </button>
                            </template>

                        ★、当我在一个父级组件中使用 <submit-button> 并且不提供任何插槽内容时：
                            
                                !-- 在父级组件模板中：不提供任何插槽内容--
                                <submit-button></submit-button>

                            但是子组件在渲染时会渲染如下内容：

                                <button type="submit">
                                    Submit
                                </button>

                        ★、当我在一个父级组件中使用 <submit-button> 并提供插槽内容时：

                                <!-- 父级组件模板中：提供插槽内容--
                                <submit-button>
                                  Save
                                </submit-button>

                                <!-- 子组件渲染：则这个提供的内容将会被渲染从而取代默认内容： --
                                <button type="submit">
                                  Save
                                </button>
            */

            /*
                   四、具名插槽

                         需要多个插槽时，可以利用 <slot>元素的一个特殊的特性：  name 来定义具名插槽。
                          
                         1、如何定义具名插槽

                                            <base-layout> 子组件模板：
                                                  <template>
                                                    <view  class="container">
                                                      <header>
                                                        !-- 我们希望把页头放这里 --
                                                        <slot name="header"></slot>
                                                      </header>
                                                      <main>
                                                        !-- 我们希望把主要内容放这里 --
                                                        <slot></slot>
                                                      </main>
                                                      <footer>
                                                        !-- 我们希望把页脚放这里 --
                                                        <slot name="footer"></slot>
                                                      </footer>
                                                    </view>
                                                  </template>

                                        注意：

                                              不要使用 v-for 对 slot 进行循环渲染处理，组件中多次出现相同name属性的 slot 会产生意料之外的问题。

                                              比如  <slot name ='footer'> 出现一次以上各家小程序中表现不同，合理的做法是传递不同name的 solt,
                                                
                                              或者在外层父组件模板中进行循环，保证多端一致。

                         2、如何指定具名插槽的内容

                                   在向具名插槽提供内容时，我们可以在一个 <template>元素上使用  v-slot指令，并以 v-slot的属性值的方式提供名称：

                                             <template>
                                                <view>
                                                !-- 父组件使用子组件`<base-layout>`，节点上使用slot特性： --
                                                  <base-layout>
                                                    <template v-slot:header>  // 具名插槽内容指定
                                                      <view>Here might be a page title</view>
                                                    </template>
                                                    <template v-slot:default> // 默认插槽内容指定
                                                      <view>A paragraph for the main content.</view>
                                                      <view>And another one.</view>
                                                    </template>
                                                    <template v-slot:footer>  // 具名查抄内容指定
                                                      <view>Here's some contact info</view>
                                                    </template>
                                                  </base-layout>
                                                </view>
                                              </template>

                                  现在  <template>元素中的所有你内容都将会被传入相应的插槽。任何没有被包裹在 "带有 v-slot 的 <template> 中的 内容" 都会被视为默认插槽的内容。

                                  （ v-slot 只能添加在 <tempatel>上！！！）

                                   ( 一个不带 name 的 <slot>出口，会钓鱼隐含的名字 "default")

                                  
                                  ★、具名插槽的缩写

                                         跟 v-on 和 v-bind 一样， v-slot 也有缩写，即把参数之前的所有内容 (v-slot:) 替换为字符 #

                                         eg:   v-slot:header  可以被简写为  #header

                                               <template>
                                                  <view>
                                                      !-- 父组件使用子组件`<base-layout>`，节点上使用slot特性： --
                                                      <base-layout>
                                                          <template #header> // v-slot:  缩写为 #
                                                              <view>Here might be a page title</view>
                                                          </template>
                                                          
                                                          <view>A paragraph for the main content.</view>
                                                          <view>And another one.</view>
                                                          
                                                          <template #footer> // v-slot:  缩写为 #
                                                              <view>Here's some contact info</view>
                                                          </template>
                                                      </base-layout>
                                                  </view>
                                              </template>
            */

            /*
                  五、作用域插槽

                      1、概述

                                    普通插槽（非作用域插槽）只能访问父组件的作用域，不能访问子组价的作用域。

                                    但是，在作用域插槽内，"父组件" 就可以访问到 "子组件的数据"

                                  
                                    有时候让插槽内容能够访问子组件中才有的数据是很有用的。

                                          !--子组件模板--
                                          <template>
                                              <view>
                                                  <slot>{{user.lastName}}</slot>
                                              </view>
                                          </tempalte>

                                    我们可能想换掉默认内容，用名而非姓来显示。如下：
                                          ！--父组件--模板
                                          <tempalte>
                                              <view>
                                                  <current-user>
                                                    {{ user.firstName }}  // 打算访问 user 这个来自子组件作用域的变量（不允许的）
                                                  </current-user>
                                              </view>
                                          </template>

                                    就像上面，插槽的默认内容是user.lastName ,但是父组件指定插槽内容的时候想让其渲染 user.firstName,但是 user 变量是来自子组件作用域的。

                                    所以，上面的代码不会生效。

                                    为了让子组件的模板中可以访问到父组件的作用域(子组件模板中可以访问 user，user为父组件作用域中的数据)，

                                    我们可以安静user作为 <slot>元素的一个 attribute 绑定上去：

                                    eg:

                                          !--子组件---
                                          <template>
                                              <view>
                                                  <slot :user="user"> {{ user.lastName }} </slot>   // 拥有属性的插槽就拥有了作用域，在指定内容时就可以自己作用域内的属性，这里是 user
                                              </view>
                                          </template>
                                          <script>
                                              export default{
                                                  data(){
                                                    return{
                                                        user:{
                                                          "lastName":"bar",
                                                          "firstName":"foo"
                                                        }
                                                    }
                                                  }
                                              }
                                          </\script>

                                      绑定在 <slot>元素上的  attribute  被称为 "插槽prop". 现在在父级作用域中，我们可以使用带值的 v-slot 来定义我们提供插槽 prop的名字：

                                          <template>
                                              <view>
                                                    <current-user>
                                                          <template v-slot：default="slotProps">  // 在指定插槽内容时，给插槽的作用域提供一个名字，这里是 slotProps
                                                            {{  slotProps.user.firstName }}
                                                          </template>
                                                    </current-user>
                                              </view>
                                          </template>

                                      在这个例子中，我们选择将包含所有插槽 prop 的对象(插槽作用域)明明为 slotProps，但你也可以使用任何你喜欢的名字。

                      2、独占默认插槽的缩写语法
                                    
                                      v-slot 是一个"指令"，
                                      
                                      v-slot:header （标注要指定内容的插槽的 name 为 "header"）

                                      #header  （是  v-slot:header 的缩写）

                                      v-slot = "slotProps" (没有任何参数的 v-slot 指令 接上 ="slotProps" 意思将默认插槽的作用域对象赋值给 soltProps 变量)

                                      ( v-slot:header="slotProps" 意思是 名为 "header" 插槽的作用域对象 赋值给 slotProps 变量)

                                  ★、就像假定未指明的内容对应默认插槽一样，不带参数的 v-slot 指令标注的插槽对应"默认插槽"
                                      
                                      eg：
                                          <template>
                                              <view>
                                                    <current-user>
                                                          <template v-slot="slotProps">  // v-slot 指令后无任何参数，表示当前指定的插槽为默认插槽
                                                              {{ slotProps.user.firstName}}
                                                          </tempale>
                                                    </current-user>
                                              </view>
                                          </template>


                                  ★、默认插槽的"缩写语法"不能和具名插槽混用，因为它会导致 "作用域不明确"。

                                      eg:   错误示例

                                            <template>
                                                <view>
                                                    !-- 无效，会导致警告 --
                                                    <current-user v-slot="slotProps">  // 此写法为独占的默认插槽
                                                        {{ slotProps.user.firstName }} 
                                                        <template v-slot:other ="otherSlotProps">
                                                            slotProps 在此处不能访问
                                                        </template>
                                                    </current-user>
                                                </view>
                                            </template>

                                  ★、只要出现多个插槽，请始终为所有插槽 "使用完整的基于 <template> 的语法"：
                                    
                                      eg:  当要同时指定多个插槽内容，要对所有插槽使用　<template> 语法

                                              <template>
                                                    <view>
                                                        <template v-slot="slotProps">
                                                            {{ slotProps.user.firstName}}
                                                        </template>

                                                        <template v-slot:other="otherSlotProps">
                                                            。。。
                                                        </template>
                                                    </view>
                                              </template>
                                     
                                              
                      3、解构插槽 Prop
                      
                                作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里：

                                      当你指定作用域插槽的作用域对象变量时  (比如：slotProps)，等于是把插槽包裹成如下的一个函数，
                                      
                                      slotProps(作用域变量名)就是函数的参数，函数内容就是插槽内容，那插槽内容就可以访问slotProps中的数据，
                                      
                                      slotProps也就成了此次指定内容的作用域对象。  

                                      slotProps 其实是子组件中定义的 "插槽属性"的集合。

                                      eg：  一次作用域插槽内容的指定，就是封装了一个下面的函数，唯一的参数就是此次的作用域对象，函数体内容就是插槽内容。
                                      
                                            function(slotProps){
                                                    
                                            }

                                这意味着 v-slot 的值实际上可以是 "任何能作为函数定义中的参数的 JavaScript 表达式"。

                                所以在支持的环境下 ( 单文件组件 或 现代浏览器)，你也可以使用 ES2015 解构 来传入具体的插槽 prop，如下：

                                        <current-user v-slot="{ user }">  // 通过解构赋值来获取某些插槽 prop 
                                            {{ user.firstName }}
                                        </current-user> 

                                这样可以使模板更简洁，尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能，例如将 user 重命名为 person ：
                                        
                                        <current-user v-slot="{ user: person }">  // 通过结构赋值来获取某些插槽 prop ，并给插槽 prop 重命名
                                          {{ person.firstName }}
                                        </current-user>

                                你甚至可以定义后备内容，用于插槽 prop 是 undefined 的情形：

                                        <current-user v-slot="{ user = { firstName: 'Guest' } }">
                                          {{ user.firstName }}
                                        </current-user>
                                       
            */

            

            
    </script>
  </body>
</html>